#region Using directives
#if DEBUG
//using NUnit.Framework;
#endif
using Microsoft.Xna.Framework.Audio;
using System;
using System.Collections.Generic;
using System.Text;
using SpaceWolf.GameLogic;
using SpaceWolf.Graphics;
using Helper;
using Microsoft.Xna.Framework.Input;
using System.Threading;
using Microsoft.Xna.Framework;
using System.IO;
#endregion

namespace SpaceWolf.Sounds
{
    /// <summary>
    /// Sound
    /// </summary>
    class Sound
    {
        #region Variables
        /// <summary>
        /// Sound stuff for XAct
        /// </summary>
        static AudioEngine audioEngine;
        /// <summary>
        /// Wave bank
        /// </summary>
        static WaveBank waveBank;
        /// <summary>
        /// Sound bank
        /// </summary>
        static SoundBank soundBank;
        #endregion

        #region Enums
        /// <summary>
        /// Sounds we use in this game. This are all the sounds and even the
        /// music, only the gear sounds are handled seperately below.
        /// </summary>
        /// <returns>Enum</returns>
        public enum Sounds
        {
            // Menu Sounds
            ButtonClick,
            ScreenClick,
            ScreenBack,
            Highlight,
            // Game Sounds
            Startsign,
            BrakeCurveMajor,
            BrakeCurveMinor,
            BrakeMajor,
            BrakeMinor,
            CarCrashMinor,
            CarCrashTotal,
            // Additional Game Sounds (gear sounds are extra)
            CheckpointBetter,
            CheckpointWorse,
            Victory,
            CarLose,
            // Music
            Music,
        } // enum Sounds
        #endregion

        #region Constructor
        /// <summary>
        /// Private constructor to prevent instantiation.
        /// </summary>
        private Sound()
        {
        } // Sound()

        /// <summary>
        /// Create sound.
        /// </summary>
        static Sound()
        {
            try
            {
                string dir = "Content\\Sounds";//Directories.SoundsDirectory;
                audioEngine = new AudioEngine(
                    Path.Combine(dir, "SpaceWolf.xgs"));
                waveBank = new WaveBank(audioEngine,
                    Path.Combine(dir, "Wave Bank.xwb"));

                // Dummy wavebank call to get rid of the warning that waveBank is
                // never used (well it is used, but only inside of XNA).
                if (waveBank != null)
                    soundBank = new SoundBank(audioEngine,
                        Path.Combine(dir, "Sound Bank.xsb"));
            } // try
            catch (Exception ex)
            {
                // Audio creation crashes in early xna versions, log it and ignore it!
                Log.Write("Failed to create sound class: " + ex.ToString());
            } // catch
        } // Sound()
        #endregion

        #region Play
        /// <summary>
        /// Play
        /// </summary>
        /// <param name="soundName">Sound name</param>
        public static void Play(string soundName)
        {
            if (soundBank == null)
                return;

            try
            {
                soundBank.PlayCue(soundName);
                //for this we have to remember cue and store it:
                //old: soundBank.GetCue(soundName).Play();
            } // try
            catch (Exception ex)
            {
                Log.Write("Playing sound " + soundName + " failed: " + ex.ToString());
            } // catch
        } // Play(soundName)

        /// <summary>
        /// Play
        /// </summary>
        /// <param name="sound">Sound</param>
        public static void Play(Sounds sound)
        {
            Play(sound.ToString());
        } // Play(sound)
        #endregion

        #region StopMusic
        /// <summary>
        /// Stop music
        /// </summary>
        public static void StopMusic()
        {
            if (soundBank == null)
                return;

            // Use a little trick, start new music, but use the cue. This will
            // replace the old music, then stop the music and everything is stopped!
            Cue musicCue = soundBank.GetCue("Music");
            musicCue.Play();
            // Wait for a short while to let Xact kick in ^^
            Thread.Sleep(10);
            musicCue.Stop(AudioStopOptions.Immediate);
        } // StopMusic()
        #endregion

        

        #region PlayCrashSound
        /// <summary>
        /// Prevent playing brake sounds on top of each other with help of this
        /// variable.
        /// </summary>
        static float crashSoundStillPlayingMs = 2000;

        /// <summary>
        /// Play crash sound
        /// </summary>
        /// <param name="totalCrash">Total crash</param>
        public static void PlayCrashSound(bool totalCrash)
        {
            // Only start new brake sound if not currently playing one
            if (crashSoundStillPlayingMs <= 0 )//&&
                // Don't play anything like this sounds in the menu mode!
                //RacingGameManager.InMenu == false)
            {
                Sound.Play(totalCrash ? Sounds.CarCrashTotal : Sounds.CarCrashMinor);
                // Wait a while for the next crash sound
                crashSoundStillPlayingMs = totalCrash ? 3456 : 2345;
            } // if (crashSoundStillPlayingMs)
        } // PlayCrashSound(totalCrash)
        #endregion

        #region Gear Sounds
        #region Gear Constants
        /// <summary>
        /// Number of gears we got in this game.
        /// </summary>
        const int NumberOfGears = 5;

        /// <summary>
        /// All gear change sounds are 1200 ms long.
        /// </summary>
        const int GearChangeSoundLengthInMs = 1200;

        /// <summary>
        /// Constants for the gear sounds
        /// </summary>
        const float stayingVol = 0.5f;//0.25f;//0.1f;
        /// <summary>
        /// Volumes for each gear, currently all set to 100% because we
        /// adjusted the gear volumes in Xact already.
        /// </summary>
        static readonly float[] vol =
            new float[NumberOfGears] { 1, 1, 1, 1, 1 };
        /// <summary>
        /// Minimum pitch for each gear, this is the sound pitch you will hear
        /// when the gear is at the very start. It goes up to maxPitch and
        /// then the next gear is initiated.
        /// </summary>
        static readonly float[] minPitch =
            //very heavy:
            //new float[NumberOfGears] { -0.75f, -0.75f, -0.69f, -0.50f, -0.41f };
            //not so strong:
            new float[NumberOfGears] { -0.375f, -0.375f, -0.345f, -0.25f, -0.205f };

        /// <summary>
        /// Max pitch for each gear.
        /// </summary>
        static readonly float[] maxPitch =
            //very heavy:
            //new float[NumberOfGears] { 0.68f, 0.54f, 0.54f, 0.49f, 0.20f };
            //not so strong:
            //new float[NumberOfGears] { 0.34f, 0.27f, 0.27f, 0.245f, 0.10f };
            //even weaker, sounds better:
            new float[NumberOfGears] { 0.24f, 0.17f, 0.17f, 0.145f, 0.10f };
        #endregion

        #region PlayGearSound
        static Cue currentGearCue = null;
        static Cue currentGearChangeCue = null;
        /// <summary>
        /// Play gear sound
        /// </summary>
        /// <param name="soundName">Gear sound type</param>
        private static void PlayGearSound(string soundName)
        {
            if (soundName.Contains("To"))
            {
                // Gear sound is automatically replaced!
                currentGearChangeCue = soundBank.GetCue(soundName);
                currentGearChangeCue.Play();
                currentGearCue = null;
            } // if (soundName.Contains)
            else
            {
                // Gear change sound is automatically replaced!
                currentGearCue = soundBank.GetCue(soundName);
                currentGearCue.Play();
                currentGearChangeCue = null;
            } // else
        } // PlayGearSound(soundName)
        #endregion

        #endregion

        #region Update
        /// <summary>
        /// Update, just calls audioEngine.Update!
        /// </summary>
        public static void Update()
        {
            //if (brakeSoundStillPlayingMs > 0)
            //    brakeSoundStillPlayingMs -= BaseGame.ElapsedTimeThisFrameInMilliseconds;
            //if (crashSoundStillPlayingMs > 0)
            //    crashSoundStillPlayingMs -= BaseGame.ElapsedTimeThisFrameInMilliseconds;

            if (audioEngine != null)
                audioEngine.Update();
        } // Update()
        #endregion

        #region Unit Testing
#if DEBUG
        /// <summary>
        /// Test play click sound
        /// </summary>
        //[Test]
        public static void TestPlayClickSound()
        {
            //int crazyCounter = 0;

            TestGame.Start(
                delegate
                {
                    //if (InputManager.MouseLeftButtonJustPressed ||
                    //    Input.GamePadAJustPressed)
                    //    Sound.Play(Sounds.ButtonClick);
                    //else if (Input.MouseRightButtonJustPressed ||
                    //    Input.GamePadBJustPressed)
                    //    Sound.Play(Sounds.Highlight);
                    //else if (Input.KeyboardKeyJustPressed(Keys.D1))
                    //    Sound.Play(Sounds.Music);
                    //else if (Input.KeyboardKeyJustPressed(Keys.D3))
                    //    Sound.Play(Sounds.BrakeCurveMajor);
                    //else if (Input.KeyboardKeyJustPressed(Keys.D4))
                    //    Sound.Play(Sounds.BrakeMinor);
                    //else if (Input.KeyboardKeyJustPressed(Keys.D5))
                    //    Sound.Play(Sounds.CarCrashTotal);
                    //else if (Input.KeyboardKeyJustPressed(Keys.D6))
                    //    Sound.Play(Sounds.Startsign);
                    /*tst
                else if (Input.KeyboardKeyJustPressed(Keys.D6))
                    Sound.Play(Sounds.Gear1);
                else if (Input.KeyboardKeyJustPressed(Keys.D7))
                {
                    Sound.Play(Sounds.Gear1ToGear2);
                    Thread.Sleep(500);
                    Sound.Play(Sounds.Gear2);
                } // else if
                else if (Input.KeyboardKeyJustPressed(Keys.D8))
                {
                    Sound.Play(Sounds.Gear2ToGear3);
                    Thread.Sleep(500);
                    Sound.Play(Sounds.Gear3);
                } // else if
                else if (Input.KeyboardKeyJustPressed(Keys.D9))
                {
                    Sound.Play(Sounds.Gear3ToGear4);
                    Thread.Sleep(500);
                    Sound.Play(Sounds.Gear4);
                } // else if
                else if (Input.KeyboardKeyJustPressed(Keys.D0))
                {
                    Sound.Play(Sounds.Gear4ToGear3);
                    Thread.Sleep(10);
                    Sound.Play(Sounds.Gear3);
                } // else if
                     */
                    Sound.Play("MainTheme");
                    Helper.Fonts.WriteText(2, 30,
                        "Press 0-9 or A/B or left/right mouse buttons to play back " +
                        "sounds!");
                });
        } // TestPlayClickSound()
#endif
        #endregion
    } // class Sound
} // RacingGameManager.Sounds
